/*
 * JGrass - Free Open Source Java GIS http://www.jgrass.org 
 * (C) HydroloGIS - www.hydrologis.com 
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Library General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Library General Public License
 * along with this library; if not, write to the Free Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.jgrasstools.gears.io.dxfdwg.libs.dwg.utils;

import java.awt.geom.Point2D;
import java.util.Vector;

/**
 * This class calculates an arc given by a start and end points and a bulge
 * 
 * @author jmorell
 */
public class ArcFromBulgeCalculator {
	private Point2D coord1, coord2;
	private Point2D center;
	private double radio, empieza, acaba;
	private double bulge;
	private double d, dd, aci;
	private Point2D coordAux;
	
	/**
	 * This method calculates an arc given by a start and end points and a bulge
	 * 
	 * @param p1 Start point of the arc given by a Point2D
	 * @param p2 End point of the arc given by a Point2D
	 * @param bulge Bulge of the arc given by a double value 
	 */
	public ArcFromBulgeCalculator(Point2D p1, Point2D p2, double bulge) {
		this.bulge = bulge;
		if (bulge < 0.0) {
			coord1 = p2;
			coord2 = p1;
		} else {
			coord1 = p1;
			coord2 = p2;
		}
		calParams();
	}
	
	private void calParams() {
		d = Math.sqrt((coord2.getX()-coord1.getX())*(coord2.getX()-coord1.getX()) + (coord2.getY()-coord1.getY())*(coord2.getY()-coord1.getY()));
		coordAux = new Point2D.Double((coord1.getX()+coord2.getX())/2.0, (coord1.getY()+coord2.getY())/2.0);
		double b = Math.abs(bulge);
		double beta = Math.atan(b);
		double alfa = beta*4.0;
		double landa = alfa/2.0;
		dd = (d/2.0)/(Math.tan(landa));
		radio = (d/2.0)/(Math.sin(landa));		
		aci = Math.atan((coord2.getX()-coord1.getX())/(coord2.getY()-coord1.getY()));
		double aciDegree = aci*180.0/Math.PI;
		if (coord2.getY() > coord1.getY()) {
			aci += Math.PI;
			aciDegree = aci*180.0/Math.PI;
		}
		center = new Point2D.Double(coordAux.getX() + dd*Math.sin(aci+(Math.PI/2.0)), coordAux.getY() + dd*Math.cos(aci+(Math.PI/2.0)));
		calEA(alfa);
	}
	
	private void calEA(double alfa){
		empieza = Math.atan2(coord1.getY()-center.getY(), coord1.getX()-center.getX());
		acaba = (empieza + alfa);
		empieza = empieza*180.0/Math.PI;
		acaba = acaba*180.0/Math.PI;
	}
	
	/**
	 * This method calculates an arc in a Gis geometry model. This arc is represented in
	 * this model by a Vector of Point2D. The distance between points in the arc is given
	 * as an argument
	 * 
	 * @param inc Distance between points in the arc
	 * @return Vector Vector with the set of Point2D that represents the arc
	 */
	public Vector getPoints(double inc) {
		Vector arc = new Vector();
		double angulo;
		int iempieza = (int) empieza + 1;
		int iacaba = (int) acaba;
		if (empieza <= acaba) {
			addNode(arc, empieza);
			for (angulo = iempieza; angulo <= iacaba; angulo += inc) {
				addNode(arc, angulo);
			}
			addNode(arc, acaba);
		} else {
			addNode(arc, empieza);
			for (angulo = iempieza ; angulo <= 360; angulo += inc) {
				addNode(arc, angulo);
			}
			for (angulo = 1; angulo <= iacaba; angulo += inc) {
				addNode(arc, angulo);
			}
			addNode(arc, angulo);
		}
		Point2D aux = (Point2D)arc.get(arc.size()-1);
		double aux1 = Math.abs(aux.getX()-coord2.getX());
		double aux2 = Math.abs(aux.getY()-coord2.getY());
		return arc;
	}
	
	/**
	 * Method that allows to obtain a set of points located in the central zone of 
	 * this arc object
	 */
	public Vector getCentralPoint() {
		Vector arc = new Vector();
		if (empieza <= acaba) {
			addNode(arc, (empieza+acaba)/2.0);
		} else {
			addNode(arc, empieza);
			double alfa = 360-empieza;
			double beta = acaba;
			double an = alfa + beta;
			double mid = an/2.0;
			if (mid<=alfa) {
				addNode(arc, empieza+mid);
			} else {
				addNode(arc, mid-alfa);
			}
		}
		return arc;
	}
	
	private void addNode(Vector arc, double angulo) {
		double yy = center.getY() + radio * Math.sin(angulo*Math.PI/180.0);
		double xx = center.getX() + radio * Math.cos(angulo*Math.PI/180.0);		
		arc.add(new Point2D.Double(xx,yy));
	}
}
